
#include "../global.h"
#include "../sharedmemory/sharedmemory.h"
#include "../log/log.h"
#include "../rsrcmanager/rsrcmanager.h"
#include "../osocklib/osocklib.h"
#include "ftal.h"


int FTAL_out_udp_header( udp_header_s* header, 
                         const filekey_t ascii_key, 
                         const off_t npacket_begin, 
                         const off_t npacket_end ) {

  if ( sizeof(ascii_key) != sizeof(header->key) ) {
    LOG_add(LOG_ERROR, "FTAL_out_udp_header: wrong key size");
    return (-1);
  }
  if ( sizeof(npacket_begin) != sizeof(header->npacket_begin) ) {
    LOG_add(LOG_ERROR, "FTAL_out_udp_header: wrong npacket_begin size");
    return (-1);
  }
  if ( sizeof(npacket_begin) != sizeof(header->npacket_end) ) {
    LOG_add(LOG_ERROR, "FTAL_out_udp_header: wrong npacket_end size");
    return (-1);
  }

  memcpy( &(header->key), &(ascii_key), sizeof(header->key) );
  header->npacket_begin = npacket_begin;
  header->npacket_end = npacket_end;

  LOG_add(LOG_DEBUG, "FTAL_out_udp_header: header filled, sizes checked, validity *NOT* checked");

  return (0);

  }


int FTAL_in_udp_header( udp_header_s* header, const void* datap ) {

  size_t tempsize;
  
  if( (header == NULL) || (datap == NULL) ) {
    LOG_add(LOG_ERROR, "FTAL_in_udp_header: NULL udp header pointer or NULL data pointer");
    return (-1);
  }
  
  tempsize = sizeof(header->key); 

  memcpy( &(header->key), datap, tempsize );
  tempsize += sizeof(header->npacket_begin);
  memcpy( &(header->npacket_begin), datap, tempsize );
  tempsize += sizeof(header->npacket_end);
  memcpy( &(header->npacket_end), datap, tempsize );
  
  LOG_add(LOG_DEBUG, "FTAL_in_udp_header: udp header structure should be filled in by now");
  return (0);

}


int FTAL_sendrsrc( localResource_s* localR,
                   off_t start,
                   off_t end, 
                   const chost_s host,
                   SM_elem_s* SM) {

  partialResource_s* pR = NULL;
  
  //void* tx_buffer = NULL;
  
  void* tx_npack_buffer = NULL;
  void* tx_temp_npack_buffer = NULL;
  msd_s sending_msd;
  off_t current_tx_offset;
  npacket_s tx_npack;
  time_t timestamp;
  float tx_speed; // bytes per second
  size_t rmc_read_retval;
  size_t size_to_read;
  int connect_retval, disconnect_retval, validate_srv_retval, fill_npack_retval, 
      osock_send_retval, out_udp_header_retval;
  ftal_tx_report SM_report;
  udp_header_s udp_header;  
  filekey_t fkey;

  if (localR == NULL) {
    LOG_add(LOG_ERROR, "FTAL_sendrsrc: NULL localResource pointer");
    return(-1);
  }

  memcpy( &fkey, &(localR->key), sizeof(localR->key) );

  if (SM == NULL) {
    LOG_add(LOG_ERROR, "FTAL_sendrsrc: NULL Shared Memory element pointer");
    return(-1);
  }

  SM->data = &SM_report;
  LOG_add(LOG_DEBUG, "FTAL_sendrsrc: successfuly linked SM_report structure to Shared Memory element !");

  if (start < 0) {
    LOG_add(LOG_WARNING, "FTAL_sendrsrc: off_t start < 0, now starting at current_tx_offset = 0");
    current_tx_offset = 0;
  }
  if (start > end) {
    LOG_add(LOG_ERROR, "FTAL_sendrsrc: start offset > end offset, exiting");
    return (-1);
  }

  current_tx_offset = start;
  LOG_add(LOG_DEBUG, 
          "FTAL_sendrsrc: current_tx_offset = %d, %d total bytes to transfert", 
          current_tx_offset, 
          (current_tx_offset - end) );
  
  SM_report.end = end;
  SM_report.current_tx_offset = 0; // default values
  SM_report.tx_speed = 0; //default values
  LOG_add(LOG_DEBUG, "FTAL_sendrsrc: TX Report Shared Memory structure initialised");

  /*
  if ((int)block_size > 0){
    tx_buffer = malloc(block_size);
  }
  else {
    LOG_add(LOG_ERROR, "FTAL_sendrsrc: invalid block_size");
    return (-1);
  }
  
  if (tx_buffer == NULL) {
    LOG_add(LOG_ERROR, "FTAL_sendrsrc: malloc() failed for tx_buffer");
    return (-1);
  }
  */

  tx_npack_buffer = malloc(MAX_NPACK_SIZE);
  tx_temp_npack_buffer = malloc(MAX_NPACK_SIZE);

  if ( (tx_npack_buffer == NULL) || (tx_temp_npack_buffer == NULL) ) {
    LOG_add(LOG_ERROR, "FTAL_sendrsrc: malloc() failed for tx_npack_buffer or tx_temp_npack_buffer");
    return (-1);
  }

  pR = RMC_create_partial_resource( fkey, start, end, localR->block_size);

  if (pR == NULL) {
    LOG_add(LOG_ERROR, "FTAL_sendrsrc: RMC_create_partial_resource() failed");
    return (-1);
  }

  LOG_add(LOG_DEBUG, "FTAL_sendrsrc: creating metasocket to send partial resource");
  connect_retval = OSOCK_connect(&sending_msd, 
                                 host.proto, 
                                 host.ipv4, 
                                 host.port, 
                                 SERVER);
  
  if (connect_retval == -1) {
    LOG_add(LOG_ERROR, "FTAL_sendrsrc: OSOCK_connect() failed");
    return (-1);
  }

  if ( host.proto == TCP ) {
    LOG_add(LOG_DEBUG, "FTAL_sendrsrc: validating TCP SERVER");
    validate_srv_retval = OSOCK_validate_tcp_server( &sending_msd );

    if (validate_srv_retval == -1) {
      LOG_add(LOG_ERROR, "FTAL_sendrsrc: OSOCK_validate_tcp_server() failed");
      return(-1);
    }
  }
 
  if (host.proto == UDP) {
    
    LOG_add(LOG_DEBUG, "FTAL_sendrsrc: UDP mode selected, now including UDP header in every network packet");
    size_to_read = ( (end - current_tx_offset) % MAX_NPACK_SIZE ) - sizeof(udp_header);
    LOG_add(LOG_DEBUG, 
            "FTAL_sendrsrc: size_to_read = %d before entering while loop (taking %d bytes of header into account)", 
            size_to_read,
            sizeof(udp_header));
    
    out_udp_header_retval = FTAL_out_udp_header( &udp_header, 
                                                 fkey, 
                                                 current_tx_offset, 
                                                 (current_tx_offset + size_to_read) );

    if (out_udp_header_retval == -1) {
      LOG_add(LOG_ERROR, "FTAL_sendrsrc: failed to fill first upd_header");
      return (-1);
    }   
  
  }

  else { // in other words: TCP mode
    
    size_to_read = (end - current_tx_offset) % MAX_NPACK_SIZE;
    LOG_add(LOG_DEBUG, "FTAL_sendrsrc: size_to_read = %d before entering while loop", size_to_read);
  
  }
  
  while (size_to_read > 0) {
 
    SM_report.current_tx_offset = current_tx_offset;
    
    if (host.proto == UDP) {
      size_to_read = ( (end - current_tx_offset) % MAX_NPACK_SIZE) - sizeof(udp_header);
    }
    else { //in other words: TCP mode
      size_to_read = (end - current_tx_offset) % MAX_NPACK_SIZE;
    }
  
    rmc_read_retval = RMC_read( pR, tx_npack_buffer, size_to_read, current_tx_offset);
    LOG_add(LOG_DEBUG, 
            "FTAL_sendrsrc: RMC_read() has read %d byte(s) of %d requested byte(s)", 
            rmc_read_retval, 
            size_to_read);
  
    if (host.proto == UDP) {
      FTAL_out_udp_header( &udp_header, fkey, current_tx_offset, (current_tx_offset + size_to_read) );
    
      if (out_udp_header_retval == -1) {
        LOG_add(LOG_WARNING, "FTAL_sendrsrc: failed to fill upd_header, retrying at next loop");
        continue;
      }  

      memcpy( tx_temp_npack_buffer, &udp_header, sizeof(udp_header) );
      memcpy( (void*) (tx_temp_npack_buffer + sizeof(udp_header)), tx_npack_buffer, rmc_read_retval); 
      
      LOG_add(LOG_DEBUG, "FTAL_sendrsrc: made an UDP network packet consisting of udp_header of %d bytes + \
                          data read from partial resource of %d bytes", sizeof(udp_header), rmc_read_retval );

      fill_npack_retval = OSOCK_fill_npacket( &tx_npack, 
                                              tx_temp_npack_buffer, 
                                              (rmc_read_retval + sizeof(udp_header) ) );
    }
    else { //in other words: TCP mode
      fill_npack_retval = OSOCK_fill_npacket( &tx_npack, tx_npack_buffer, rmc_read_retval);
    }
    
    if (fill_npack_retval == -1) {
      LOG_add(LOG_WARNING, 
              "FTAL_sendrsrc: failed to fill current network packet, retrying at current_tx_offset = %d",
              current_tx_offset);
      continue;
    }
    
    timestamp = time(NULL);

    osock_send_retval = OSOCK_send( &sending_msd, &tx_npack );

    timestamp = time(NULL) - timestamp;
    tx_speed = OSOCK_get_npacket_length( &tx_npack ) / timestamp;
    LOG_add(LOG_DEBUG, "FTAL_sendrsrc: tx_speed = %f for last transmitted network packet", tx_speed);
  
    SM_report.tx_speed = tx_speed;

    if (osock_send_retval == -1) {
      LOG_add(LOG_WARNING, 
              "FTAL_sendrsrc: failed to send current network packet, retrying at current_tx_offset = %d",
              current_tx_offset);
      continue;
    }

    LOG_add(LOG_DEBUG, 
            "FTAL_sendrsrc: read %d bytes from resource, a network packet of %d bytes was sent",
            rmc_read_retval,
            OSOCK_get_npacket_length(&tx_npack) );
  
    current_tx_offset += OSOCK_get_npacket_length(&tx_npack);
    LOG_add(LOG_DEBUG, "FTAL_sendrsrc: new current_tx_offset = %d, processing next loop iteration", current_tx_offset);
  
  }
 
  LOG_add(LOG_DEBUG, "FTAL_sendrsrc: partial resource sent 100%, exiting FTAL_sendrsrc()...");

  RMC_destroy_partial_resource( pR );
  
  //free(tx_buffer);
  
  free(tx_npack_buffer);
  free(tx_temp_npack_buffer);
  disconnect_retval = OSOCK_disconnect( &sending_msd );

  if (disconnect_retval == -1)
    LOG_add(LOG_WARNING, "FTAL_sendrsrc: OSOCK_disconnect failed, continue anyway but might cause problems");
  
  return (0);

}


int FTAL_recvrsrc( localResource_s* localR,
                   off_t start,
                   off_t end, 
                   const chost_s host,
                   SM_elem_s* SM) {

/*
2 RMC_write pour vider le rx_buffer
1 OSOCK_receive() pour remplir le rx_buffer
1bis extraire le header si on recoit en udp
3 enfile une zone valide
3bis met a jour l'offset courant
4 defile une zone valide vers pere via la shm si c'est libre
5 boucle sur 1 jusqu'a atteindre 'end'
6 termine de defiler les zones valides si il en reste
*/
  
  return(0);
  
}


/* end of ftal.c */

